package com.qc31.baselib.net.https

import java.io.IOException
import java.io.InputStream
import java.net.HttpURLConnection
import java.net.URL
import java.security.KeyManagementException
import java.security.KeyStore
import java.security.NoSuchAlgorithmException
import java.security.cert.Certificate
import java.security.cert.CertificateException
import java.security.cert.CertificateFactory
import java.security.cert.X509Certificate
import javax.net.ssl.*

/**
 *@Author Jasper
 *@Time 2021/10/11 11:28
 *@Desc
 */
object HttpsUtil {
    class SSLParams {
        var sSLSocketFactory: SSLSocketFactory? = null
        var trustManager: X509TrustManager? = null
    }

    fun getSslSocketFactory(): SSLParams {
        return getSslSocketFactoryBase(null, null, null)
    }

    /**
     * https单向认证
     * 可以额外配置信任服务端的证书策略，否则默认是按CA证书去验证的，若不是CA可信任的证书，则无法通过验证
     */
    fun getSslSocketFactory(trustManager: X509TrustManager?): SSLParams? {
        return getSslSocketFactoryBase(trustManager, null, null)
    }

    /**
     * https单向认证
     * 用含有服务端公钥的证书校验服务端证书
     */
    fun getSslSocketFactory(vararg certificates: InputStream?): SSLParams? {
        return getSslSocketFactoryBase(null, null, null, *certificates)
    }

    /**
     * https双向认证
     * bksFile 和 password -> 客户端使用bks证书校验服务端证书
     * certificates -> 用含有服务端公钥的证书校验服务端证书
     */
    fun getSslSocketFactory(
        bksFile: InputStream?,
        password: String?,
        vararg certificates: InputStream?
    ): SSLParams? {
        return getSslSocketFactoryBase(null, bksFile, password, *certificates)
    }

    /**
     * https双向认证
     * bksFile 和 password -> 客户端使用bks证书校验服务端证书
     * X509TrustManager -> 如果需要自己校验，那么可以自己实现相关校验，如果不需要自己校验，那么传null即可
     */
    fun getSslSocketFactory(
        bksFile: InputStream?,
        password: String?,
        trustManager: X509TrustManager?
    ): SSLParams? {
        return getSslSocketFactoryBase(trustManager, bksFile, password)
    }

    private fun getSslSocketFactoryBase(
        trustManager: X509TrustManager?,
        bksFile: InputStream?,
        password: String?,
        vararg certificates: InputStream?
    ): SSLParams {
        val sslParams = SSLParams()
        return try {
            val keyManagers: Array<KeyManager>? = prepareKeyManager(bksFile, password)
            val trustManagers: Array<TrustManager>? = prepareTrustManager(*certificates)
            val manager: X509TrustManager? = //优先使用用户自定义的TrustManager
                trustManager
                    ?: if (trustManagers != null) {
                        //然后使用默认的TrustManager
//                        chooseTrustManager(trustManagers)
                        MX509TrustManager()
                    } else {
                        //否则使用不安全的TrustManager
                        MX509TrustManager()
                    }
            // 创建TLS类型的SSLContext对象， that uses our TrustManager
            val sslContext: SSLContext = SSLContext.getInstance("TLS")
            // 用上面得到的trustManagers初始化SSLContext，这样sslContext就会信任keyStore中的证书
            // 第一个参数是授权的密钥管理器，用来授权验证，比如授权自签名的证书验证。第二个是被授权的证书管理器，用来验证服务器端的证书
            sslContext.init(keyManagers, arrayOf<TrustManager?>(manager), null)
            // 通过sslContext获取SSLSocketFactory对象
            sslParams.sSLSocketFactory = sslContext.socketFactory
            sslParams.trustManager = manager
            sslParams
        } catch (e: NoSuchAlgorithmException) {
            throw AssertionError(e)
        } catch (e: KeyManagementException) {
            throw AssertionError(e)
        }
    }

    private fun prepareKeyManager(bksFile: InputStream?, password: String?): Array<KeyManager>? {
        try {
            if (bksFile == null || password == null) return null
            val clientKeyStore: KeyStore = KeyStore.getInstance("BKS")
            clientKeyStore.load(bksFile, password.toCharArray())
            val kmf: KeyManagerFactory =
                KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())
            kmf.init(clientKeyStore, password.toCharArray())
            return kmf.keyManagers
        } catch (e: Exception) {
            print(e.message)
        }
        return null
    }

    private fun prepareTrustManager(vararg certificates: InputStream?): Array<TrustManager>? {
        if (certificates.isNotEmpty()) return null
        try {
            val certificateFactory: CertificateFactory = CertificateFactory.getInstance("X.509")
            // 创建一个默认类型的KeyStore，存储我们信任的证书
            val keyStore: KeyStore = KeyStore.getInstance(KeyStore.getDefaultType())
            keyStore.load(null)
            for ((index, certStream) in certificates.withIndex()) {
                val certificateAlias = index.toString()
                // 证书工厂根据证书文件的流生成证书 cert
                val cert: Certificate = certificateFactory.generateCertificate(certStream)
                // 将 cert 作为可信证书放入到keyStore中
                keyStore.setCertificateEntry(certificateAlias, cert)
                try {
                    certStream?.close()
                } catch (e: IOException) {
                    print(e.message)
                }
            }
            //我们创建一个默认类型的TrustManagerFactory
            val tmf: TrustManagerFactory =
                TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
            //用我们之前的keyStore实例初始化TrustManagerFactory，这样tmf就会信任keyStore中的证书
            tmf.init(keyStore)
            //通过tmf获取TrustManager数组，TrustManager也会信任keyStore中的证书
            return tmf.trustManagers
        } catch (e: Exception) {
            print(e.message)
        }
        return null
    }

    private fun chooseTrustManager(trustManagers: Array<TrustManager>): X509TrustManager? {
        for (trustManager in trustManagers) {
            if (trustManager is X509TrustManager) {
                return trustManager as X509TrustManager
            }
        }
        return null
    }


    /**
     * 设置 https 请求
     * @throws Exception
     */
    @Throws(java.lang.Exception::class)
    private fun trustAllHttpsCertificates() {
        HttpsURLConnection.setDefaultHostnameVerifier { str, session -> true }
        val trustAllCerts = arrayOfNulls<TrustManager>(1)
        val tm: TrustManager = SslTrustManager()
        trustAllCerts[0] = tm
        val sc = SSLContext.getInstance("SSL")
        sc.init(null, trustAllCerts, null)
        HttpsURLConnection.setDefaultSSLSocketFactory(
            sc.socketFactory
        )
    }


    internal class SslTrustManager : TrustManager, X509TrustManager {
        override fun getAcceptedIssuers(): Array<X509Certificate> {
            return arrayOf()
        }

        fun isServerTrusted(
            certs: Array<X509Certificate?>?
        ): Boolean {
            return true
        }

        fun isClientTrusted(
            certs: Array<X509Certificate?>?
        ): Boolean {
            return true
        }

        @Throws(CertificateException::class)
        override fun checkServerTrusted(
            certs: Array<X509Certificate>, authType: String
        ) {
            return
        }

        @Throws(CertificateException::class)
        override fun checkClientTrusted(
            certs: Array<X509Certificate>, authType: String
        ) {
            return
        }
    }


    fun isTrue(imgUrl: String?): Boolean {
        val responseCode = try {
            val url = URL(imgUrl)
            trustAllHttpsCertificates()
            val urlCon: HttpURLConnection = url.openConnection() as HttpURLConnection
            //这里设置超时时间，防止一直不返回结果，就一直等待
            urlCon.connectTimeout = 3000
            urlCon.responseCode
        } catch (e: java.lang.Exception) {
            println("url errror...:" + e.message)
            return false
        }
        return if (responseCode == HttpURLConnection.HTTP_OK) {
            println("url is ok!")
            true
        } else {
            println("Bad url...")
            false
        }
    }


}